using System;
using System.Text;
using System.Threading;
using System.Collections.Generic;

namespace Foundations.Net.Pooling
{

    internal class ObjectPool<T> where T : class
    {
        List<T> _pool;
        
        readonly Int32 MIN_SIZE;
        const Int32 MAX_SIZE = 64;
        readonly Int32 MAX_TIME_OUT;
 
        ActivationDelegate<T> _activate;
        ConstructorDelegate<T> _construct;
        DeActivationDelegate<T> _deactivate;

        List<AutoResetEvent> _waitHandles;
        ReaderWriterLock lck = new ReaderWriterLock();

        internal ObjectPool(Int32 Capacity)
        {
            if (Capacity > MAX_SIZE)
            {
                MIN_SIZE = MAX_SIZE;
            }
            else
            {
                MIN_SIZE = Capacity;
            }
            
            MAX_TIME_OUT = Timeout.Infinite;
            _pool = new List<T>(MIN_SIZE);
            _waitHandles = new List<AutoResetEvent>(MIN_SIZE);
         }

        internal ObjectPool(Int32 Capacity, Int32 TimeOut)
        {
            if (Capacity > MAX_SIZE)
            {
                MIN_SIZE = MAX_SIZE;
            }
            else
            {
                MIN_SIZE = Capacity;
            }

            MAX_TIME_OUT = TimeOut;
            _pool = new List<T>(MIN_SIZE);
            _waitHandles = new List<AutoResetEvent>(MIN_SIZE);
        }

        internal void Init(ConstructorDelegate<T> Construct, ActivationDelegate<T> Activate, DeActivationDelegate<T> DeActivate)
        {
            lck.AcquireWriterLock(Timeout.Infinite);
            {
                try
                {
                    _activate = Activate;
                    _construct = Construct;
                    _deactivate = DeActivate;

              
                    for (Int32 index = MIN_SIZE; index > 0; index--)
                    {
                        Add(true);
                    }
                }
                catch 
                {
                    throw;
                }
                finally
                {
                    lck.ReleaseWriterLock();
                }

            }             
        }

        private Int32 Add(bool State)
        {
            if (_pool.Count < _pool.Capacity)
            {
                _pool.Add(_construct());
                _waitHandles.Add(new AutoResetEvent(State));
                return (_pool.Count - 1);
            }

            throw new InvalidOperationException("request exceeds pool MAX_SIZE");
        }

        internal PooledObject<T> Get()
        {
           
            try
            {
                lck.AcquireReaderLock(Timeout.Infinite);
                {
                    int index = -1;

                    try
                    {
                        index = WaitHandle.WaitAny(_waitHandles.ToArray(), MAX_TIME_OUT);
                        if (index == WaitHandle.WaitTimeout)
                        {
                            throw new TimeoutException();
                        }
                    }
                    catch (TimeoutException)
                    {
                        throw;
                    }
                    finally
                    {
                        lck.ReleaseReaderLock();
                    }

                    return new PooledObject<T>(_pool[index], _waitHandles[index],_activate, _deactivate);
               }
            }
            catch (ApplicationException)
            {
                throw;
            }
            catch
            {
                throw;
            }
        }
        
    }
}
